﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;

namespace CitrixPVSWrapper
{
    public static partial class PvsHelpers 
    {

        public static List<string[]> ExecuteMcliQuery(QueryDefinition Query, Boolean Verbose = false, Boolean Debug = false)
        {
            return ExecuteMcliQuery(new Collection<QueryDefinition> { Query }, Verbose, Debug);
        }

        public static List<string[]> ExecuteMcliQuery(Collection<QueryDefinition> Queries, Boolean Verbose = false, Boolean Debug = false)
        {
            if (Verbose) { Console.WriteLine("VERBOSE: Executing MCli Queries. Verbose mode enabled"); }
            if (Verbose) { Console.WriteLine("VERBOSE: Total queries provided: " + Queries.Count()); }

            // Set up the PowerShell session to execute the queries
            RunspaceConfiguration runConfig = RunspaceConfiguration.Create();
            PSSnapInException psEx = null;
            runConfig.AddPSSnapIn("mclipssnapin", out psEx);
            Runspace runspace = RunspaceFactory.CreateRunspace(runConfig);
            runspace.Open();

            // Collection to hold all the results
            List<string[]> Records = new List<string[]>();

            // Execute all of the queries
            foreach (QueryDefinition Query in Queries)
            {
                if (Verbose) {
                    Console.WriteLine("VERBOSE: Starting Query. Query Type is " + Query.Action.ToString());
                    Console.WriteLine("VERBOSE: Target Object is: " + Query.TargetObject.ToString());
                    Console.WriteLine("VERBOSE: Parameters: " + String.Join(", ", Query.Parameters));
                }

                string QueryScript = "";
                switch (Query.Action)
                {
                    case QueryActionType.Get: QueryScript = "mcli-get"; break;
                    case QueryActionType.Info: QueryScript = "mcli-info"; break;
                    case QueryActionType.RunWithReturn: QueryScript = "mcli-runwithreturn"; break; 
                }

                QueryScript += " -Type " + Query.TargetObject;
                if (Query.Parameters.Count() > 0) { QueryScript += " -p " + String.Join(",",Query.Parameters); }
                QueryScript += " -ErrorAction SilentlyContinue";

                Pipeline pipeline = runspace.CreatePipeline();
                pipeline.Commands.AddScript(QueryScript);
                List<string> QueryResult = pipeline.Invoke().Select(r => r.ToString()).ToList();
                
                if (Verbose) { Console.WriteLine("VERBOSE: Query executed. Lines in result: " + QueryResult.Count()); }              

                // Inject any provided values into each of the records. If there are no values to inject, then just copy the results to the output stream
                List<string> QueryResults = new List<string>();
                if (Query.InjectValues.Count > 0)
                {
                    foreach (int RecordStart in Enumerable.Range(0, QueryResult.Count).Where(i => QueryResult[i].StartsWith("Record #")).ToList())
                    {
                        int RecordEnd = QueryResult.GetRange(RecordStart, QueryResult.Count - RecordStart).IndexOf("");
                        if (RecordEnd == -1) { RecordEnd = QueryResult.Count; }
                        QueryResults = QueryResults.Concat(QueryResult.GetRange(RecordStart,RecordEnd)).ToList();
                        foreach (DictionaryEntry Inject in Query.InjectValues) { QueryResults.Add(Inject.Key + ": " + Inject.Value); }
                        QueryResults.Add("");
                    }
                }
                else
                {
                    QueryResults = QueryResults.Concat(QueryResult).ToList();
                }

                if (Verbose) { Console.WriteLine("VERBOSE: Query complete. Total lines in output: " + QueryResults.Count); }

                // Depending on the query type, the values that we need to return can be different. Get's provide a list of strings. RunWithReturn need to have a single value extracted.
                switch (Query.Action)
                {
                    case QueryActionType.Get: case QueryActionType.Info:
                        // Convert the collection of strings into a collection of records. This saves all of the repeat enumerations in each of the functions. 
                        foreach (int RecordStart in Enumerable.Range(0, QueryResults.Count).Where(i => QueryResults[i].StartsWith("Record #")).ToList())
                        {
                            if (Debug) { Console.WriteLine("DEBUG: Record Start: " + RecordStart); }
                            int RecordEnd = QueryResults.GetRange(RecordStart, QueryResults.Count - RecordStart).IndexOf("");
                            if (RecordEnd == -1) { RecordEnd = QueryResults.Count; }
                            else { RecordEnd = RecordStart + RecordEnd; }
                            if (Debug) { Console.WriteLine("DEBUG: Record End: " + RecordEnd); }
                            string[] RecordData = QueryResults.GetRange(RecordStart, RecordEnd - RecordStart).ToArray();
                            Records.Add(RecordData);
                        }
                        if (Verbose) { Console.WriteLine("VERBOSE: Record conversion complete. Total records: " + Records.Count); }
                        break;
                    
                    case QueryActionType.RunWithReturn:
                        foreach (int RecordStart in Enumerable.Range(0, QueryResults.Count).Where(i => QueryResults[i].StartsWith("RunWithReturn succeeded.")).ToList())
                        {
                            if (Debug) { Console.WriteLine("DEBUG: Record Start: " + RecordStart); }
                            string[] RecordData = new string[1];
                            RecordData[0] = Query.TargetObject.ToString() + ": " + QueryResults[RecordStart].Split('=')[1].ToString().Trim();
                            Records.Add(RecordData);
                        }
                        if (Verbose) { Console.WriteLine("VERBOSE: Record conversion complete. Total records: " + Records.Count); }
                        break;

                }
                
            }

            // Tidy up the Powershell session
            runspace.Close();
            return Records;
        }
    }
}
